/*
 * File:    Set.java
 * Created: 04-Feb-2009
 * Version: $Id$
 *
 * COPYRIGHT (C) 2009, Bitgate Mobile, LLC.  All Rights Reserved.
 *
 * software@bitgatemobile.com
 */

package com.webplasm.render.tags.u;

import static com.bitgate.util.debug.Debug.debug;

import java.util.Arrays;
import java.util.List;

import com.webplasm.context.RenderContext;
import com.webplasm.render.engine.RenderException;
import com.webplasm.render.tags.TagInterface;
import com.webplasm.render.tags.WrappedNode;
import com.webplasm.store.VariableStoreInterface;
import com.webplasm.store.VariableContainer.VariableType;

/**
 * This tag assigns a variable with renderable contents.
 * 
 * @author kenji
 * @since Webplasm 1.0
 */
public class Set extends WrappedNode implements TagInterface {
	private static final String DESCRIPTION = "Assigns variable contents.";
	
	public String getNamespace() {
		return "u";
	}

	/**
	 * The attributes are listed in the table in this section.
	 * <p/>
	 * <table border="1">
	 * <tr><th>Name:</th>
	 *     <th>Role:</th></tr>
	 * <tr><td>var</td>
	 *     <td>This is the name of the variable to set a value to.  The value is in the body of the tag.</td></tr>
	 * <tr><td>type</td>
	 *     <td>This is the type of variable desired.  Valid types are:<br/>
	 *         <li>&quot;string&quot; - a string value.</li>
	 *         <li>&quot;map&quot; or &quot;hashmap&quot; - a key/value pair hashmap storage type.</li>
	 *         <li>&quot;sql&quot; - a database stored object, returned as a string representation of the request.</li>
	 *         <li>&quot;xml&quot; or &quot;node&quot; - an XML parsed document that can be used with XPath parsing.</li></td></tr>
	 * <tr><td>key</td>
	 *     <td>This is the key name for a mappable object (map/hashmap)</td></tr>
	 * <tr><td>keytype</td>
	 *     <td>Used in conjunction with key, you can set a string, sql, or xml object.  Hash/Hashmap is not (yet) allowed.</td></tr>
	 * </table>
	 * <p/>
	 * <u>Note:</u> If you want to perform an actual SQL query, please refer to the &lt;u:sql&gt; element.  The &quot;sql&quot;
	 * type in this instance does not refer to actual SQL-stored variables, it refers to SQL requests that were made.  If you
	 * want to use session-based variables, use the &lt;u:session&gt; element.
	 * 
	 * @return <code>List&lt;String&gt;</code> containing all known attributes.
	 */
	public List<String> getAttributes() {
		return Arrays.asList("var", "type", "key", "keytype");
	}
	
	public List<String> getManagedNodes() {
		return null;
	}

	public String getDescription() {
		return DESCRIPTION;
	}

	@Override
	public StringBuffer render(RenderContext rContext) throws RenderException {
		StringBuffer setBuffer = super.render(rContext);
		
		if (elements.get("var") == null) {
			throw new RenderException("u:set requires a 'var' element in order to assign a resulting value.");
		}
		
		if (elements.get("var").equals("server_uuid")) {
			throw new RenderException("var element cannot be an assigned word.");
		}
		
		VariableType vType = VariableType.STRING;
		
		if (elements.get("type") != null) {
			String variableTypeString = elements.get("type").toUpperCase();
			boolean found = false;
			
			for(VariableType varTypeEntry : VariableType.values()) {
				if (varTypeEntry.name().equals(variableTypeString)) {
					vType = varTypeEntry;
					found = true;
					break;
				}
			}
			
			if (!found) {
				debug("Variable type '" + variableTypeString + "' does not exist, defaulting to String.");
			}
		}
		
		VariableStoreInterface vsInterface = rContext.getVariableStore().getVariable(elements.get("var"), vType);
		
		if (elements.get("key") != null) {
			if (!vsInterface.isMappable()) {
				debug("u:set exception: attempting to assign a key to type '" + vType + "' that is not mappable.");
				
				throw new RenderException("u:set exception: specified variable '" + elements.get("var") + "' of type '" +
						elements.get("type") + "' is not mappable.");
			}
			
			VariableType vKeyType = VariableType.STRING;
			
			if (elements.get("keytype") != null) {
				String variableTypeString = elements.get("keytype").toUpperCase();
				boolean found = false;
				
				for(VariableType varTypeEntry : VariableType.values()) {
					if (varTypeEntry.name().equals(variableTypeString)) {
						vKeyType = varTypeEntry;
						found = true;
						break;
					}
				}
				
				if (!found) {
					debug("Variable type '" + variableTypeString + "' does not exist, defaulting to String.");
				}
			}
			
			VariableStoreInterface vsKeyInterface = vKeyType.getVariableStoreInterface();
			
			if (vsKeyInterface.isStandard()) {
				vsKeyInterface.setValue(super.render(rContext).toString());
				vsInterface.getMap().put(elements.get("key"), vsKeyInterface);
			}
			
			debug("Set variable '" + elements.get("var") + "' type '" + vsKeyInterface.getType() + 
					"' key '" + elements.get("key") + "' value '" + vsKeyInterface.getString() + "'");
		} else {
			if (vsInterface.isStandard()) {
				vsInterface.setValue(setBuffer.toString());
			} else if (vsInterface.isArray()) {
				VariableStoreInterface vsInterfaceArray = VariableType.STRING.getVariableStoreInterface();
				
				vsInterfaceArray.setValue(super.render(rContext).toString());
				vsInterface.setValue(vsInterfaceArray);
			}
			
			debug("Set variable '" + elements.get("var") + "' type '" + vType.name() + "' to '" +
					setBuffer.toString() + "'");
		}
		
		return new StringBuffer();
	}
}